package cn.com.twoke.application.game.mario.core.ecs.system;

import cn.com.twoke.application.game.mario.core.ecs.component.InputState;
import cn.com.twoke.application.game.mario.core.ecs.component.PositionComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.render.RenderComponent;
import cn.com.twoke.application.game.mario.core.ecs.component.render.RenderComponentHandler;
import cn.com.twoke.application.game.mario.core.ecs.component.render.RenderHandler;
import cn.com.twoke.application.game.mario.core.ecs.entity.Entity;
import cn.com.twoke.application.game.mario.game.component.PlayerComponent;
import cn.com.twoke.application.game.mario.game.component.SceneComponent;
import cn.com.twoke.application.game.mario.game.scene.CameraPayload;
import cn.com.twoke.application.game.mario.game.scene.CameraType;
import cn.com.twoke.application.game.mario.game.scene.SceneManager;
import cn.com.twoke.application.game.mario.game.util.PlayerUtils;
import cn.com.twoke.application.game.mario.game.config.Tiles;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 渲染系统
 */
@Slf4j
@Getter
@Builder
public class RenderSystem implements RenderGameSystem {
    @Builder.Default
    private static Map<Class<? extends RenderComponent>, RenderComponentHandler> handlers = new HashMap<>();
    private final InputState inputState;
    private final SceneManager sceneManager;
    @Builder.Default
    Map<Integer, List<RenderComponent>> components = new HashMap<>();
    @Setter
    private JPanel container;
    @Builder.Default
     private List<RenderHook> afterHooks = new ArrayList<>();
     
    // 渲染缓存相关字段
    private BufferedImage renderCache;
    private boolean cacheDirty = true;
    private int lastMapWidth;
    private int lastMapHeight;
    
    // 资源管理
    private final List<BufferedImage> temporaryImages = new ArrayList<>();
    
    // 渲染完成回调
    private Runnable renderCompleteCallback;
    
    // 是否已完成首次渲染
    private boolean firstRenderCompleted = false;

    @FunctionalInterface
    public interface RenderHook {
        void draw(Entity entity, Graphics2D g2d);
    }

    public RenderSystem addAfterHook(RenderHook afterRenderHook) {
        this.afterHooks.add(afterRenderHook);
        // 添加钩子后标记缓存为脏数据
        this.cacheDirty = true;
        return this;
    }
    
    /**
     * 设置渲染完成回调
     * @param callback 回调函数
     */
    public void setRenderCompleteCallback(Runnable callback) {
        this.renderCompleteCallback = callback;
    }

    public static synchronized RenderComponentHandler getRenderComponentHandler(RenderComponent component) {
        try {
            RenderComponentHandler handler;
            if (handlers.containsKey(component.getClass())) {
                handler = handlers.get(component.getClass());
            } else {
                Class<? extends RenderComponentHandler> renderComponentHandlerClass = Opt.ofNullable(AnnotationUtil.getAnnotation(component.getClass(), RenderHandler.class))
                        .map(RenderHandler::value)
                        .orElseThrow(() -> new RuntimeException("RenderHandler not found for component: " + component.getClass().getName()));
                RenderComponentHandler renderComponentHandler1 = ReflectUtil.newInstance(renderComponentHandlerClass);
                handler = handlers.put(component.getClass(), renderComponentHandler1);
            }
            return handler;
        } catch (Exception e) {
            log.error("Error getting render component handler for component: {}", component.getClass().getName(), e);
            throw new RuntimeException("Failed to get render component handler", e);
        }
    }

    @Override
    public void render(List<Entity> entities, Graphics2D cameraGraphics) {
        int mapWidth = Tiles.widthOf(211);
        int mapHeight = Tiles.heightOf(30);
        
        // 检查是否需要重新创建缓存图像
        if (renderCache == null || cacheDirty || 
            mapWidth != lastMapWidth || mapHeight != lastMapHeight) {
            // 释放旧的缓存图像
            if (renderCache != null) {
                renderCache.flush();
            }
            renderCache = new BufferedImage(mapWidth, mapHeight, BufferedImage.TYPE_INT_ARGB);
            lastMapWidth = mapWidth;
            lastMapHeight = mapHeight;
            cacheDirty = false;
        }
        
        // 清除缓存图像
        Graphics2D cacheGraphics = renderCache.createGraphics();
        cacheGraphics.setComposite(AlphaComposite.Clear);
        cacheGraphics.fillRect(0, 0, mapWidth, mapHeight);
        cacheGraphics.setComposite(AlphaComposite.SrcOver);
        
        drawToScreen(entities, renderCache, cameraGraphics, this.doRender(renderCache, entities));
        cacheGraphics.dispose();
        
        // 首次渲染完成时调用回调
        if (!firstRenderCompleted) {
            firstRenderCompleted = true;
            if (renderCompleteCallback != null) {
                renderCompleteCallback.run();
            }
        }
    }

    private void renderEntity(Entity entity, BufferedImage cache, Graphics2D g2d) {
        List<RenderComponent> components = getRenderComponentsByEntity(entity);
        if (CollUtil.isNotEmpty(components)) {
            components.stream().filter(RenderComponent::isDisplay).sorted().forEach(renderComponent -> {
                doRenderComponent(entity, cache, g2d, renderComponent);
            });
        }
        this.afterHooks.forEach(hook -> hook.draw(entity, g2d));
    }

    private void doRenderComponent(Entity entity, BufferedImage cache, Graphics2D g2d, RenderComponent component) {
        try {
            RenderComponentHandler handler = getRenderComponentHandler(component);
            if (Opt.ofNullable(handler).isPresent()) {
                handler.handle(entity, cache, g2d, component);
            }
        } catch (Exception e) {
            log.error("Error rendering component for entity ID: {}", entity.getId(), e);
        }
    }

    private List<RenderComponent> getRenderComponentsByEntity(Entity entity) {
        if (components.containsKey(entity.getId())) {
            return components.get(entity.getId());
        } else {
            List<RenderComponent> components1 = entity.getComponents(item -> item instanceof RenderComponent).stream().map(item -> (RenderComponent) item).collect(Collectors.toList());
            return components.put(entity.getId(), components1);
        }
    }

    private Consumer<Graphics2D> doRender(BufferedImage cache, List<Entity> entities) {
        return (g) -> {
            // 绘制实体
            for (Entity entity : entities) {
                renderEntity(entity, cache, g);
            }
        };
    }

    private void drawToScreen(List<Entity> entities, BufferedImage cache, Graphics screenGraphics, Consumer<Graphics2D> renderHandler) {
        try {
            /**
             * 缓存
             */
            Graphics g = cache.getGraphics();

            renderHandler.accept((Graphics2D) g);

            // 使用相机来切割页面
            Entity player = PlayerUtils.getPlayer(entities);
            if (player == null) {
                log.warn("Player entity not found");
                g.dispose();
                return;
            }
            
            PlayerComponent playerComponent = player.getComponent(PlayerComponent.class);
            SceneComponent sceneComponent = player.getComponent(SceneComponent.class);
            PositionComponent positionComponent = player.getComponent(PositionComponent.class);

            int cameraPosX = 0;
            int cameraPosY = 0;
            CameraPayload cameraPayload = sceneManager.getSceneCamera(sceneComponent.getName());
            int cameraSizeW = (int) cameraPayload.getSize().x;
            int cameraSizeH = (int) cameraPayload.getSize().y;

            if (cameraPayload.getCameraType() == CameraType.FIXED) { // 固定相机
                cameraPosX = (int) cameraPayload.getPosition().x;
                cameraPosY = (int) cameraPayload.getPosition().y;
            } else if (cameraPayload.getCameraType() == CameraType.FOLLOW) { // 跟随相机
                if (positionComponent.x - cameraPayload.getOffset().x > 0) {
                    cameraPosX = (int) (positionComponent.x - cameraPayload.getOffset().x);
                }
                if (cameraPosX + cameraSizeW > cache.getWidth()) {
                    cameraPosX = cache.getWidth() - cameraSizeW;
                }
            }

            if (SceneComponent.isDebug()) { // 调试
                // 绘制完整地图
                Rectangle rectangle = new Rectangle(cameraPosX, cameraPosY, cameraSizeW, cameraSizeH);
                // 绘制相机位置
                g.setColor(new Color(255, 0, 0, 80));
                ((Graphics2D) g).fill(rectangle);
                g.setColor(Color.white);
                ((Graphics2D) g).draw(rectangle);
                if (cameraPosX + this.container.getWidth() > cache.getWidth()) {
                    screenGraphics.drawImage(cache.getSubimage(cache.getWidth() - this.container.getWidth(), 0,  this.container.getWidth(), cache.getHeight()), 0, 0, this.container);
                } else {
                    screenGraphics.drawImage(cache.getSubimage(cameraPosX, 0, this.container.getWidth(), cache.getHeight()), 0, 0, this.container);
                }
            } else {
                screenGraphics.drawImage(cache.getSubimage(cameraPosX, cameraPosY, cameraSizeW, cameraSizeH), (this.container.getWidth() - cameraSizeW) / 2, (this.container.getHeight() - cameraSizeH) / 2, this.container);
            }

            // 绘制说明
            screenGraphics.setColor(Color.WHITE);
            screenGraphics.drawString(StrUtil.format("玩家名称: {}", playerComponent.name), 10, 20);
            screenGraphics.drawString("操作: WASD移动", 10, 40);
            screenGraphics.drawString(StrUtil.format("位置: ({},{})", positionComponent.x, positionComponent.y), 10, 60);
            
            g.dispose();
        } catch (Exception e) {
            log.error("Error in drawToScreen", e);
        }
    }
    
    /**
     * 标记渲染缓存为脏数据，需要重新绘制
     */
    public void markCacheDirty() {
        this.cacheDirty = true;
    }
    
    /**
     * 清理临时资源
     */
    public void cleanup() {
        // 清理临时图像资源
        temporaryImages.clear();
        
        // 清理缓存图像
        if (renderCache != null) {
            renderCache.flush();
            renderCache = null;
        }
    }
    
    /**
     * 添加临时图像资源以便后续清理
     * @param image 临时图像
     */
    public void addTemporaryImage(BufferedImage image) {
        if (image != null) {
            temporaryImages.add(image);
        }
    }
}